package vn.vfriends.mcms.admin.bean;

import eu.medsea.util.MimeUtil;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTPFile;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
import org.primefaces.model.UploadedFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import vn.vfriends.mcms.admin.model.Site;
import vn.vfriends.mcms.admin.model.SiteDoc;
import vn.vfriends.mcms.admin.model.User;
import vn.vfriends.mcms.admin.service.FTPFileService;
import vn.vfriends.mcms.admin.service.SiteService;

/**
 *
 * @author tuan@vfriends.vn
 */
@Named
@ConversationScoped
public class EditorBean implements Serializable {

    static private final Logger logger = LoggerFactory.getLogger(EditorBean.class);
    @Inject
    private SiteService siteService;
    @Inject
    private FTPFileService ftpFileService;
    @Inject
    private Conversation conversation;
    private TreeNode root;
    private TreeNode selectedNode;
    private String nodeNewName;
    private static String LOCAL_PATH;
    private static boolean USING_FTP;
    private String selectedDocumentContent;
    private String selectedDocumentMimeType;
    private String selectedImagePath;
    private String createdNodeName;
    private String createdNodeType = "File";

    @PostConstruct
    public void init() {
        if (conversation.isTransient()) {
            conversation.begin();
        }
        FacesContext context = FacesContext.getCurrentInstance();
        ServletContext servletContext = (ServletContext) context.getExternalContext().getContext();
        LOCAL_PATH = servletContext.getInitParameter("LOCAL_PATH");
        USING_FTP = Boolean.valueOf(servletContext.getInitParameter("USING_FTP"));
        initTree();
    }

    public TreeNode getRoot() {
        return root;
    }

    public Conversation getConversation() {
        return conversation;
    }

    public String getSelectedImagePath() {
        return selectedImagePath;
    }

    public String getSelectedDocumentContent() {
        return selectedDocumentContent;
    }

    public void setSelectedDocumentContent(String selectedDocumentContent) {
        this.selectedDocumentContent = selectedDocumentContent;
    }

    public String getSelectedDocumentMimeType() {
        return selectedDocumentMimeType;
    }

    public String getCreatedNodeName() {
        return createdNodeName;
    }

    public void setCreatedNodeName(String createdNodeName) {
        this.createdNodeName = createdNodeName;
    }

    public String getCreatedNodeType() {
        return createdNodeType;
    }

    public void setCreatedNodeType(String createdNodeType) {
        this.createdNodeType = createdNodeType;
    }

    private void initTree() {
        root = new DefaultTreeNode("root", null);

        FacesContext context = FacesContext.getCurrentInstance();
        try {
            HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
            HttpSession session = request.getSession(false);
            User loggedUser = (User) session.getAttribute("LOGGED_USER");
            List<Site> siteList = siteService.findSitesByUser(loggedUser);

            for (Site site : siteList) {
                //  Check existence of logged user's sites
                File siteFile = new File(FilenameUtils.concat(LOCAL_PATH, site.getName()));
                if (!siteFile.exists() || siteFile.listFiles().length == 0) {
                    FileUtils.forceMkdir(siteFile);

                    if(USING_FTP) {
                        writeLocalSite(File.separator + site.getName());
                    }
                }

                TreeNode siteNode = new DefaultTreeNode(new SiteDoc(site.getName(), File.separator + site.getName(), false), root);
                File[] files = siteFile.listFiles();
                for (File file : files) {
                    this.initNode(siteNode, file, File.separator + site.getName());
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    private void writeLocalSite(String path) throws Exception {
        FTPFile[] ftpFiles = this.ftpFileService.listFiles(path);
        for (FTPFile ftpFile : ftpFiles) {
            this.writeFile(ftpFile, path);
        }
    }

    private void writeFile(FTPFile ftpFile, String path) throws Exception {
        File file = new File(FilenameUtils.concat(LOCAL_PATH + path, ftpFile.getName()));
        if (ftpFile.isFile()) {
            if (!file.exists()) {
                file.createNewFile();
            }
            OutputStream outStream = null;
            try {
                outStream = new FileOutputStream(file);
                this.ftpFileService.retrieveFile(FilenameUtils.concat(path, ftpFile.getName()), outStream);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                throw e;
            } finally {
                IOUtils.closeQuietly(outStream);
            }
        } else {
            FileUtils.forceMkdir(file);
            FTPFile[] ftpFiles = this.ftpFileService.listFiles(FilenameUtils.concat(path, file.getName()));
            for (FTPFile childFile : ftpFiles) {
                this.writeFile(childFile, FilenameUtils.concat(path, file.getName()));
            }
        }
    }

    private void initNode(TreeNode node, File file, String path) throws Exception {
        if (file.isFile()) {
            SiteDoc doc = new SiteDoc(file.getName(), FilenameUtils.concat(path, file.getName()), true);
            String type = (doc.getType() == null) ? SiteDoc.Type.DOCUMENT.toString() : doc.getType().toString();
            TreeNode child = new DefaultTreeNode(type, doc, node);
        } else {
            SiteDoc doc = new SiteDoc(file.getName(), FilenameUtils.concat(path, file.getName()), false);
            TreeNode child = new DefaultTreeNode(doc, node);
            File[] files = file.listFiles();
            for (File childFile : files) {
                this.initNode(child, childFile, FilenameUtils.concat(path, file.getName()));
            }
        }
    }

    public TreeNode getSelectedNode() {
        return selectedNode;
    }

    public void setSelectedNode(TreeNode selectedNode) {
        this.selectedNode = selectedNode;
    }

    public String getNodeNewName() {
        return nodeNewName;
    }

    public void setNodeNewName(String nodeNewName) {
        this.nodeNewName = nodeNewName;
    }

    public void deleteNodeAction() {
        try {
            if (this.selectedNode != null && !this.selectedNode.getParent().equals(root)) {
                this.removeNode(selectedNode);
                this.selectedNode = null;
                //  Refresh tree node
                this.initTree();
            } else {
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Node này không được xoá", ""));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
        }
    }

    private void removeNode(TreeNode node) throws Exception {
        if (node.getChildCount() == 0) {
            SiteDoc selectedDoc = (SiteDoc) node.getData();
            //  Remove on local
            File file = new File(LOCAL_PATH + selectedDoc.getPath());
            if (file.exists()) {
                file.delete();
            }
            // Remove on FTP Server
            if(USING_FTP) {
                if (selectedDoc.isFile()) {
                    this.ftpFileService.deleteFile(selectedDoc.getPath());
                } else {
                    this.ftpFileService.removeDirectory(selectedDoc.getPath());
                }
            }
        } else {
            List<TreeNode> nodeList = node.getChildren();
            for (TreeNode child : nodeList) {
                this.removeNode(child);
            }
            SiteDoc selectedDoc = (SiteDoc) node.getData();
            //  Remove on local
            File file = new File(LOCAL_PATH + selectedDoc.getPath());
            if (file.exists()) {
                file.delete();
            }
            // Remove on FTP Server
            if(USING_FTP) {
                this.ftpFileService.removeDirectory(selectedDoc.getPath());
            }
        }
    }

    public void renameNodeAction() {
        try {
            if (this.selectedNode != null) {
                if (!this.selectedNode.getParent().equals(root)) {
                    SiteDoc selectedDoc = (SiteDoc) this.selectedNode.getData();
                    String oldPath = selectedDoc.getPath();
                    String newPath = FilenameUtils.concat(FilenameUtils.getFullPathNoEndSeparator(oldPath), this.nodeNewName);

                    //  Rename on local
                    File oldFile = new File(LOCAL_PATH + oldPath);
                    File newFile = new File(LOCAL_PATH + newPath);
                    oldFile.renameTo(newFile);
                    //  Rename on FTP Server
                    if(USING_FTP) {
                        this.ftpFileService.rename(oldPath, newPath);
                    }
                    this.nodeNewName = "";
                    //  Refresh tree node
                    this.initTree();
                } else {
                    FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Node này không được đổi tên", ""));
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
        }
    }

    public void viewNodeAction() {
        this.selectedDocumentContent = "";
        this.selectedDocumentMimeType = "";
        this.selectedImagePath = "";
        try {
            if (this.selectedNode != null) {
                SiteDoc selectedDoc = (SiteDoc) this.selectedNode.getData();
                if (selectedDoc.getType().equals(SiteDoc.Type.IMAGE)) {
                    this.selectedImagePath = LOCAL_PATH + selectedDoc.getPath();
                } else if (selectedDoc.getType().equals(SiteDoc.Type.DOCUMENT) && selectedDoc.isFile()) {
                    File file = new File(LOCAL_PATH + selectedDoc.getPath());
                    selectedDocumentContent = FileUtils.readFileToString(file, "UTF-8");
                    selectedDocumentMimeType = MimeUtil.getFirstMimeType(MimeUtil.getMimeType(file));
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
        }
    }

    public void saveDocumentAction() {
        try {
            if (this.selectedNode != null) {
                SiteDoc selectedDoc = (SiteDoc) this.selectedNode.getData();
                File file = new File(LOCAL_PATH + selectedDoc.getPath());
                //  Saving file on local
                FileUtils.writeStringToFile(file, selectedDocumentContent, "UTF-8");
                //  Write file on FTP server
                if(USING_FTP) {
                    ByteArrayInputStream input = new ByteArrayInputStream(this.selectedDocumentContent.getBytes("UTF-8"));
                    this.ftpFileService.storeFile(selectedDoc.getPath(), input);
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
        }
    }

    public void fileUploadAction(FileUploadEvent event) {
        try {
            if (this.selectedNode != null) {
                SiteDoc selectedDoc = (SiteDoc) this.selectedNode.getData();
                String basePath = "";
                if (selectedDoc.isFile()) {
                    basePath = FilenameUtils.getFullPathNoEndSeparator(selectedDoc.getPath());
                } else {
                    basePath = selectedDoc.getPath();
                }

                UploadedFile uploadedFile = event.getFile();
                String path = FilenameUtils.concat(LOCAL_PATH + basePath, uploadedFile.getFileName());
                File file = new File(path);
                //  Saving file on local
                FileUtils.writeByteArrayToFile(file, uploadedFile.getContents());
                //  Write file on FTP server
                if(USING_FTP) {
                    ByteArrayInputStream input = new ByteArrayInputStream(uploadedFile.getContents());
                    this.ftpFileService.storeFile(FilenameUtils.concat(basePath, uploadedFile.getFileName()), input);
                }
                //  Refresh tree node
                this.initTree();
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
        }
    }
    
    public void createNodeAction() {
        try {
            if (this.selectedNode != null) {
                SiteDoc selectedDoc = (SiteDoc) this.selectedNode.getData();
                String basePath = "";
                if (selectedDoc.isFile()) {
                    basePath = FilenameUtils.getFullPathNoEndSeparator(selectedDoc.getPath());
                } else {
                    basePath = selectedDoc.getPath();
                }
                
                String path = FilenameUtils.concat(basePath, this.createdNodeName);
                
                File file = new File(LOCAL_PATH + path);
                
                if(this.createdNodeType.equals("File")) {
                    //  Create file on local
                    file.createNewFile();
                    //  Create file on FTP server
                    if (USING_FTP) {
                        InputStream input = FileUtils.openInputStream(file);
                        this.ftpFileService.storeFile(path, input);
                    }
                } else {
                    //  Create directory on local
                    file.mkdir();
                    //  Create file on FTP server
                    if(USING_FTP) {
                        this.ftpFileService.makeDirectory(path);
                    }
                }
                
                //  Refresh tree node
                this.initTree();
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
        }
    }
    
    public void deploySiteAction() {
        try {
            if (this.selectedNode != null) {
                SiteDoc selectedDoc = (SiteDoc) this.selectedNode.getData();
                String path = selectedDoc.getPath();
                String[] tmp = path.split("/");
                String siteName = tmp[1];
                this.siteService.deploySite(siteName);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
        }
    }
}
